#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <cliopt.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
typedef struct {
	int		next_frame;
	uint32_t	planesize[4];
	uint32_t	framesize;
	uint8_t *	alloc_plane[4];
} raw_hnd_t;

static inline char *get_filename_extension(char *filename)
{
	char *ext = filename + strlen(filename);
	while( *ext != '.' && ext > filename )
		ext--;
	ext += *ext == '.';
	return ext;
}

static int raw_input_init(char *input_filename, cli_opt_t *opt)
{
	char *ext = get_filename_extension(input_filename);
	if (strcmp(ext, "yuv") == 0) {
		opt->i_csp = C_CSP_I420;
	} else if (strcmp(ext, "nv21") == 0) {
		opt->i_csp = C_CSP_NV21;
	} else if (strcmp(ext, "nv12") == 0) {
		opt->i_csp = C_CSP_NV12;
	} else if (strcmp(ext, "t420") == 0) {
		opt->i_csp = C_CSP_T420;
	} else {
		fprintf(stderr, "can't supported input file color space\n");
		goto err_unsupport_csp;
	}
	return 0;

err_unsupport_csp:
	return -1;
}


static int raw_open_file(char *filename, cli_opt_t *opt)
{
	uint64_t filesize = 0;
	int framesize = 0;
	int framecnt = 0;
	raw_hnd_t *hnd;
	int i = 0;
	if (raw_input_init(filename, opt) < 0)
		goto err_raw_input_init;
	hnd = malloc(sizeof(*hnd));
	if (hnd == NULL) {
		fprintf(stderr, "malloc raw_hnd_t failed:%s\n", strerror(errno));
		goto err_mallc_hnd;
	}
	memset(hnd, 0, sizeof(*hnd));

	hnd->next_frame = 0;

	if (opt->i_csp != C_CSP_I420) {
		for (i = 0; i < 2; i++) {
			hnd->planesize[i] = (opt->image_width * opt->image_height) >> i;
		}
	} else {
		hnd->planesize[0] = (opt->image_width * opt->image_height);
		hnd->planesize[1] = (opt->image_width * opt->image_height) >> 2;
		hnd->planesize[2] = (opt->image_width * opt->image_height) >> 2;
	}
	framesize =
	hnd->framesize = opt->image_width * opt->image_height * 3 / 2;

	if ((opt->input_file  = fopen(filename, "r")) == NULL) {
		fprintf(stderr, "open %s failed:%s\n", filename, strerror(errno));
		goto err_fopen_inputfile;
	}
	opt->hin = (hnd_t)hnd;

	fseek(opt->input_file, 0, SEEK_END);
	filesize = ftell(opt->input_file);
	fseek(opt->input_file, 0, SEEK_SET);
	framecnt = filesize/framesize;
	if (opt->totalframe != 0) {
		if (opt->totalframe > framecnt)
			opt->totalframe = framecnt;
	} else
		opt->totalframe = framecnt;
	return 0;

err_fopen_inputfile:
	free(hnd);
err_mallc_hnd:
err_raw_input_init:
	return -1;
}

static int raw_picture_alloc(i264e_pic_t *pic, cli_opt_t *opt)
{
	int i = 0;

	memset(pic, 0, sizeof(*pic));
	pic->img.i_csp = opt->i_csp;
	pic->img.i_plane = 2;
	pic->i_pic_struct = C_PIC_STRUCT_AUTO;

	if (pic->img.i_csp == C_CSP_NV12) {
		pic->img.i_stride[0] = C_ALIGN(opt->image_width, 16);
		pic->img.i_stride[1] = C_ALIGN(opt->image_width, 16);
		pic->img.i_lines[0] = C_ALIGN(opt->image_height, 16);
		pic->img.i_lines[1] = C_ALIGN(opt->image_height, 16)>>1;
	} else if (pic->img.i_csp == C_CSP_T420) {
		pic->img.i_stride[0] = C_ALIGN(opt->image_width, 16)*16;
		pic->img.i_stride[1] = C_ALIGN(opt->image_width, 16)*8;
		pic->img.i_lines[0] = C_ALIGN(opt->image_height, 16)>>4;
		pic->img.i_lines[1] = C_ALIGN(opt->image_height, 16)>>4;
	} else if (pic->img.i_csp == C_CSP_NV21) {
		pic->img.i_stride[0] = C_ALIGN(opt->image_width, 16);
		pic->img.i_stride[1] = C_ALIGN(opt->image_width, 16);
		pic->img.i_lines[0] = C_ALIGN(opt->image_height, 16);
		pic->img.i_lines[1] = C_ALIGN(opt->image_height, 16)>>1;
	} else if (pic->img.i_csp == C_CSP_I420) {
		pic->img.i_plane = 3;
		pic->img.i_stride[0] = C_ALIGN(opt->image_width, 16);
		pic->img.i_stride[1] = C_ALIGN(opt->image_width, 16)>>1;
		pic->img.i_stride[2] = C_ALIGN(opt->image_width, 16)>>1;
		pic->img.i_lines[0] = C_ALIGN(opt->image_height, 16);
		pic->img.i_lines[1] = C_ALIGN(opt->image_height, 16)>>1;
		pic->img.i_lines[2] = C_ALIGN(opt->image_height, 16)>>1;
	}
	for (i = 0; i < pic->img.i_plane; i++) {
		pic->img.alloc_plane[i] = opt->ckmalloc(-1, pic->img.i_stride[i] * pic->img.i_lines[i] + C_VB_ALIGN, C_VB_ALIGN);
		if (pic->img.alloc_plane[i] == NULL) {
			goto err_opt_malloc;
		}
		pic->img.plane[i] = (uint8_t *)(C_ALIGN((intptr_t)(pic->img.alloc_plane[i]), C_VB_ALIGN));
		pic->img.is_vir = opt->b_use_phy;
	}
	return 0;

err_opt_malloc:
	for (; i >= 0; i--) {
		opt->ckfree(-1, pic->img.alloc_plane[i]);
	}
	memset(pic, 0, sizeof(*pic));

	return -1;
}

static int raw_read_frame(i264e_pic_t *pic, cli_opt_t *opt)
{
	raw_hnd_t *hnd = (raw_hnd_t *)opt->hin;
	int i = 0;

	if (fseek(opt->input_file, hnd->next_frame * hnd->framesize, SEEK_SET) < 0) {
		fprintf(stderr, "fseek frame number:%d failed:%s\n", hnd->next_frame, strerror(errno));
		goto err_fseek_frame_number;
	}

	for (i = 0; i < pic->img.i_plane; i++) {
		if (fread(pic->img.plane[i], 1, hnd->planesize[i], opt->input_file) != hnd->planesize[i]) {
			fprintf(stderr, "fread frame size:%d failed:%s\n", hnd->planesize[i], strerror(errno));
			goto err_fread_frame_size;
		}
	}
	hnd->next_frame++;

	return 0;

err_fread_frame_size:
err_fseek_frame_number:
	return -1;
}

static int raw_release_frame(i264e_pic_t *pic, cli_opt_t *opt)
{
	return 0;
}

static void raw_picture_clean(i264e_pic_t *pic, cli_opt_t *opt)
{
	int i = 0;
	for (i = 0; i < pic->img.i_plane; i++) {
		opt->ckfree(-1, pic->img.alloc_plane[i]);
	}
}

static int raw_close_file(cli_opt_t *opt)
{
	fclose(opt->input_file);
	free(opt->hin);

	return 0;
}

const cli_input_t cli_input_raw = {raw_open_file, raw_picture_alloc, raw_read_frame,
	raw_release_frame, raw_picture_clean, raw_close_file};
